//
// 
// 有 n 个城市，其中一些彼此相连，另一些没有相连。如果城市 a 与城市 b 直接相连，且城市 b 与城市 c 直接相连，那么城市 a 与城市 c 间接相连
//。 
//
// 省份 是一组直接或间接相连的城市，组内不含其他没有相连的城市。 
//
// 给你一个 n x n 的矩阵 isConnected ，其中 isConnected[i][j] = 1 表示第 i 个城市和第 j 个城市直接相连，而 
//isConnected[i][j] = 0 表示二者不直接相连。 
//
// 返回矩阵中 省份 的数量。 
//
// 
//
// 示例 1： 
//
// 
//输入：isConnected = [[1,1,0],[1,1,0],[0,0,1]]
//输出：2
// 
//
// 示例 2： 
//
// 
//输入：isConnected = [[1,0,0],[0,1,0],[0,0,1]]
//输出：3
// 
//
// 
//
// 提示： 
//
// 
// 1 <= n <= 200 
// n == isConnected.length 
// n == isConnected[i].length 
// isConnected[i][j] 为 1 或 0 
// isConnected[i][i] == 1 
// isConnected[i][j] == isConnected[j][i] 
// 
// 
// 
// Related Topics深度优先搜索 | 广度优先搜索 | 并查集 | 图 
//
// 👍 872, 👎 0 
//
//
//
//

package leetcode.editor.cn;

import java.util.LinkedList;
import java.util.List;

class NumberOfProvinces {
    public static void main(String[] args) {
        Solution solution = new NumberOfProvinces().new Solution();
        // solution.findCircleNum(new int[][]{{1, 1, 0}, {1, 1, 0}, {0, 0, 1}});
        solution.findCircleNum(new int[][]{{1, 1, 1}, {1, 1, 1}, {1, 1, 1}});
    }

    //leetcode submit region begin(Prohibit modification and deletion)
    class Solution {
        // DFS：遍历所有城市，对于每个城市，如果该城市尚未被访问过，则从该城市开始深度优先搜索，
        // 通过矩阵 isConnected 得到与该城市直接相连的城市有哪些，这些城市和该城市属于同一个连通分量，
        // 然后对这些城市继续深度优先搜索，直到同一个连通分量的所有城市都被访问到，即可得到一个省份。
        // 遍历完全部城市以后，即可得到连通分量的总数，即省份的总数。
        /*public int findCircleNum(int[][] isConnected) {
            int m = isConnected.length, n = isConnected[0].length;
            List<Integer>[] graph = new LinkedList[m];
            for (int i = 0; i < m; i++) {
                graph[i] = new LinkedList<>();
            }

            for (int i = 0; i < m; i++) {
                for (int j = 0; j < n; j++) {
                    if (i != j && isConnected[i][j] == 1) graph[i].add(j);
                }
            }

            boolean[] visited = new boolean[m];
            int res = 0;
            for (int i = 0; i < m; i++) {
                if (!visited[i]) {
                    dfs(graph, i, visited);
                    res++;
                }
            }

            return res;
        }

        public void dfs(List<Integer>[] graph, int cur, boolean[] visited) {
            visited[cur] = true;
            for (Integer next : graph[cur]) {
                if (!visited[next]) {
                    dfs(graph, next, visited);
                }
            }
        }*/

        // 并查集：模板题
        public int findCircleNum(int[][] isConnected) {
            int m = isConnected.length, n = isConnected[0].length;
            UF uf = new UF(m);
            for (int i = 0; i < m; i++) {
                for (int j = i + 1; j < n; j++) {
                    if (isConnected[i][j] == 1) uf.union(i, j);
                }
            }

            return uf.getCount();
        }

        class UF {
            int[] parent;
            int count;

            public UF(int n) {
                this.count = n;
                this.parent = new int[n];
                for (int i = 0; i < n; i++) {
                    parent[i] = i;
                }
            }

            public void union(int p, int q) {
                int rootP = find(p);
                int rootQ = find(q);
                if (rootP != rootQ) {
                    parent[rootQ] = rootP;
                    // 连通分量减一
                    count--;
                }
            }

            public int find(int x) {
                if (x != parent[x]) {
                    parent[x] = find(parent[x]);
                }
                return parent[x];
            }

            public boolean isConnection(int p, int q) {
                int rootP = find(p);
                int rootQ = find(q);
                return rootP == rootQ;
            }

            public int getCount() {
                return count;
            }

        }
    }
//leetcode submit region end(Prohibit modification and deletion)

}
